package com.dm.layui.im.controller;

import com.alibaba.fastjson.JSONObject;
import com.dm.layui.im.constant.SocketConstant;
import com.dm.layui.im.entity.*;
import com.dm.layui.im.service.ChatMsgService;
import com.dm.layui.im.service.GroupsService;
import com.dm.layui.im.service.MineService;
import com.dm.layui.im.util.DateFormat;
import com.dm.layui.im.util.IdGenerat;
import com.dm.layui.im.util.LayimUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @ServerEndpoint 注解是一个类层次的注解，它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 * @ServerEndpoint 可以把当前类变成websocket服务类
 */
@Controller
@ServerEndpoint(value = "/websocket/{userno}")
public class ChatWebSocket {

    private static ChatMsgService chatMsgService;
    private static GroupsService groupsService;
    private static MineService mineService;
    private static RedisTemplate redisTemplate;

    @Autowired
    public void setChatService(ChatMsgService chatService,GroupsService groupsService,MineService mineService, RedisTemplate redisTemplate) {
        ChatWebSocket.chatMsgService = chatService;
        ChatWebSocket.groupsService=groupsService;
        ChatWebSocket.mineService=mineService;
        ChatWebSocket.redisTemplate=redisTemplate;
    }

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    private static ConcurrentHashMap<String, ChatWebSocket> webSocketSet = new ConcurrentHashMap<String, ChatWebSocket>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session WebSocketsession;
    //当前发消息的人员编号
    private String userno = "";


    /**
     * 连接建立成功调用的方法
     *
     * session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam(value = "userno") String param, Session WebSocketsession, EndpointConfig config) {
        userno = param;//接收到发送消息的人员编号
        this.WebSocketsession = WebSocketsession;
        webSocketSet.put(param, this);//加入map中
        addOnlineCount();     //在线数加1 System.out.println("有新连接加入！当前在线人数为" + getOnlineCount());
        System.out.println("OnOpen");
        mineService.upUserMine(new Mine().setId(userno).setStatus("online")); //更新用户的状态为在线
        //获取离线消息
        getOnLineMsg();
        //获取消息盒子
        getMsgBox();
    }

    /**
     * 获取离线消息
     */
    private void getOnLineMsg(){
        //从redis中取离线接收的消息
        String prefix=userno+"_"+ SocketConstant.ON_LINE_MESSAGE +"*";
        // 获取所有的key
        Set<String> keys = redisTemplate.keys(prefix);
        if(keys.size()!=0){
            //遍历key
            for(String str:keys){
                //获取消息数据
                Map<String,Object> chatMsgMap = redisTemplate.opsForHash().entries(str);
                String sendId = chatMsgMap.get("sendId").toString();
                String content = chatMsgMap.get("content").toString();
                String receiveId = chatMsgMap.get("receiveId").toString();
                Date sendTime = new Date(Long.valueOf(chatMsgMap.get("sendTime").toString()));
                //获取登录人信息 填充对象
                Mine userInfo = mineService.getUserInfo(sendId);
                userInfo.setToid(receiveId).setContent(content).setSendtime(sendTime).setTimeStamp(sendTime.getTime());
                if(str.contains("friend")){
                    userInfo.setType("friend");
                }else  if(str.contains("group")){
                    userInfo.setType("group");
                }
                try{
                    //转成json形式发送出去
                    SocketMsgType socketMsgType=new SocketMsgType().setCode(200).setMsgType("发送成功").setMsgType( SocketConstant.ON_LINE_MESSAGE ).setData(userInfo);
                    webSocketSet.get(userno).sendMessage(JSONObject.toJSONString(socketMsgType));
                    redisTemplate.delete(str);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取未读消息
     */
    public void getMsgBox(){
        //从redis中取离线接收的消息
        String prefix=userno+"_"+ SocketConstant.ADD_ASK +"*";
        // 获取所有的key
        Set<String> keys = redisTemplate.keys(prefix);
        //未读消息个数
        int Unread=0;

        if(keys.size()!=0) {
            //遍历key
            for (String str : keys) {
                //获取消息数据
                Map<String,Object> addAsk = redisTemplate.opsForHash().entries(str);
                String read = addAsk.get("read").toString();
                if(read.equals("0")){
                    Unread++;
                    //设为已读
//                    redisTemplate.opsForHash().put(str,"read","1");
                }
            }
        }

        try{
            //转成json形式发送出去
            SocketMsgType socketMsgType=new SocketMsgType().setCode(200).setMsgType("发送成功").setMsgType( SocketConstant.MSG_BOX_UNREAD ).setData(Unread);
            webSocketSet.get(userno).sendMessage(JSONObject.toJSONString(socketMsgType));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (!userno.equals("")) {
            webSocketSet.remove(userno); //从set中删除
            subOnlineCount();     //在线数减1  System.out.println("有一连接关闭！当前在线人数为" + getOnlineCount());
            System.out.println("onClose");
            mineService.upUserMine(new Mine().setId(userno).setStatus("offline")); //更新用户的状态为离线
        }
    }


    /**
     //给指定的人发消息
     * @param session 可选的参数
     */
    @SuppressWarnings("unused")
	@OnMessage
    public void onMessage(String mine, Session session) {
        //接收前台发送过来的消息
        JSONObject jsonObject = JSONObject.parseObject(mine);
        String msgtype=jsonObject.getString("msgType");
        if(msgtype.equals("chatMsg")){
            Mine message=jsonObject.toJavaObject(Mine.class);
            //查看是单反消息还是和群发消息
            if(message.getType().equals("friend")){
                sendToUser(message);
            }else if(message.getType().equals("group")){
                sendAll(message);
            }
        }else if(msgtype.equals("addAsk")){
            LayimAsk layimAsk=jsonObject.toJavaObject(LayimAsk.class);
            addAsk(layimAsk);
        }
    }

    /**
     * 添加申请
     */
    public void addAsk(LayimAsk layimAsk){
        //添加好友申请
        if(layimAsk.getType().equals("0")){

        }else{//加群申请
            //查群主
            Groups groupInfo = groupsService.getById(layimAsk.getFromGroup());
            layimAsk.setUid(groupInfo.getOwner());
            String content=layimAsk.getContent()+"  "+groupInfo.getGroupname();
            layimAsk.setContent(content);
        }
        String time=String.valueOf(new Date().getTime());
        Mine userInfo = mineService.getUserInfo(layimAsk.getFrom());
        String uid=layimAsk.getUid();
        layimAsk.setId(IdGenerat.getGeneratID())
                .setTime(time).setHref("")
                .setUser(userInfo);
        boolean onLine=webSocketSet.get(uid) != null;
        //接收人在线直接发送
        if (onLine) {
            layimAsk.setRead("1");
        }else {//不在线
            layimAsk.setRead("0");
        }
        Map<String, Object> map = LayimUtil.beanToMap(layimAsk);
        String key=uid+"_"+SocketConstant.ADD_ASK+"_"+time+"_"+layimAsk.getId()+"_friend";
        redisTemplate.opsForHash().putAll(key,map);

        try {
            //接收人在线直接发送
            if (onLine) {
                SocketMsgType socketMsgType=new SocketMsgType().setCode(200).setMsgType("发送成功").setMsgType(SocketConstant.ADD_ASK).setData(null);
                webSocketSet.get(uid).sendMessage(JSONObject.toJSONString(socketMsgType));//转成json形式发送出去
            }else {//不在线
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 给指定的人发送消息
     *
     * @param message
     */
    public static void sendToUser(Mine message) {
        System.out.println("单点消息");
        message.setSendtime(new Date());
        Date date=message.getSendtime();
        String id=message.getId();
        String content=message.getContent();
        String reviceUserId = message.getToid();
        //填充消息对象
        ChatMsg chatMsg = new ChatMsg().setMsgType("0").setReciveUserId(reviceUserId).setSendUserId(id).setContent(content).setCreateTime(date);
        //发消息
        chatMsgService.insertChatmsg(chatMsg);
        try {
            //接收人在线直接发送
            if (webSocketSet.get(reviceUserId) != null) {
                SocketMsgType socketMsgType=new SocketMsgType().setCode(200).setMsgType("发送成功").setMsgType( SocketConstant.ON_LINE_MESSAGE ).setData(message);
                webSocketSet.get(reviceUserId).sendMessage(JSONObject.toJSONString(socketMsgType));//转成json形式发送出去
            }else {//不在线
                //放入redis处理不在线
                Map<String,Object> map=new HashMap<>();
                String time=String.valueOf(date.getTime());
                String Key=reviceUserId+"_"+ SocketConstant.ON_LINE_MESSAGE +"_"+time+"_friend";
                map.put("receiveId",reviceUserId);
                map.put("sendId",id);
                map.put("content",content);
                map.put("sendTime",date.getTime());
//                map.put("msgType",message.getContent());
                redisTemplate.opsForHash().putAll(Key,map);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 给群组中的所有人发消息
     *
     * @param message
     */
    private void sendAll(Mine message) {
        System.out.println("群消息");
        message.setSendtime(new Date());
        String revicegid = message.getToid();
        Date date=message.getSendtime();
        String id=message.getId();
        String content=message.getContent();
        List<Mine> userlist = groupsService.getGroupUsre(revicegid); //此群中的用户（包含自己）
        groupsService.InsertGroupMsg(new GroupMsg().setSendUserId(message.getId()).setGroupId(message.getToid()).setContent(message.getContent()));
        try {
            for (Mine uid:userlist) {
                String userId= uid.getId();
                if(!userId.equals(message.getId())){//（过滤掉自己）
                    if (webSocketSet.get(userId) != null) {
                        SocketMsgType socketMsgType=new SocketMsgType().setCode(200).setMsgType("发送成功").setMsgType( SocketConstant.ON_LINE_MESSAGE ).setData(message);
                        webSocketSet.get(userId).sendMessage(JSONObject.toJSONString(socketMsgType));//转成json形式发送出去
                    }else {//不在线
                        //放入redis处理不在线
                        Map<String,Object> map=new HashMap<>();
                        String time=String.valueOf(date.getTime());
                        String Key=userId+"_"+ SocketConstant.ON_LINE_MESSAGE +"_"+time+"_group";
                        map.put("receiveId",revicegid);
                        map.put("sendId",id);
                        map.put("content",content);
                        map.put("sendTime",date.getTime());
//                map.put("msgType",message.getContent());
                        redisTemplate.opsForHash().putAll(Key,map);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 自定义消息
     * */
    public static void addFriend(String fid,Mine mine) throws IOException {
        //在线
        if (webSocketSet.get(fid) != null) {
            try {
                SocketMsgType socketMsgType = new SocketMsgType().setCode(200).setMsgType("addFriend").setMsg("添加好友成功").setData(mine);
                //这里可以设定只推送给这个sid的，为null则全部推送
                if (mine != null) {
                    webSocketSet.get(fid).sendMessage(JSONObject.toJSONString(socketMsgType));
                }else{
                    socketMsgType.setMsg("添加好友失败").setData("0");
                    webSocketSet.get(fid).sendMessage(JSONObject.toJSONString(socketMsgType));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            //不在线
            String time=String.valueOf(new Date().getTime());
            String Key=fid+"_"+ SocketConstant.ADD_ASK  +"_"+time;
            Map<String,Object> map=new HashMap<>();
            map.put("read","0");
            redisTemplate.opsForHash().putAll(Key,map);
        }
    }


    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }


    /**
     * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.WebSocketsession.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }


    public static synchronized int getOnlineCount() {
        return onlineCount;
    }


    public static synchronized void addOnlineCount() {
        ChatWebSocket.onlineCount++;
    }


    public static synchronized void subOnlineCount() {
        ChatWebSocket.onlineCount--;
    }

}

